﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using GPI.Models;
using GPI.ViewModel;
using System.Text;
using System.IO;
using GPI.Tools;
using System.Transactions;

namespace GPI.Controllers
{ 
    public class ProjectController : Controller
    {
        private Entities DB;
        private Entities db
        {
            get
            {
                if (DB == null)
                {
                    DB = Entities.GetEntitiesForUser(User);
                }
                return DB;
            }
        }


        private void SetViewBagBoolProperties()
        {
            SetViewBagBoolProperties(-1);
        }

        private void SetViewBagBoolProperties(int idProj)
        {
            GPI_PERIOD p = db.GPI_PERIOD.Single();
            ViewBag.open =  p.start_date < DateTime.Now && p.end_date > DateTime.Now;
            ViewBag.prof = User.IsInRole("Prof");
            if (User.IsInRole("Student"))
            {
                ViewBag.CrtStudentProjectID = db.GPI_STUDENT.Find(User.Identity.Name).FK_Project;
                ViewBag.subscribed = idProj == ViewBag.CrtStudentProjectID;
            }
            else
            {
                ViewBag.subscribed = false;
            }
        }


        //
        // GET: /Project/
        [Authorize]
        public ViewResult Index()
        {
            string user = User.Identity.Name;
            var gpi_project = db.GPI_PROJECT.Include(g => g.GPI_PROF).Include(g => g.GPI_PROF1);
            SetViewBagBoolProperties();

            return View(gpi_project.ToList());
        }

        //
        // GET: /Project/Details/5
        
        [Authorize]
        public ViewResult Details(int id, bool? maxReached)
        {
            ViewBag.maxReached = maxReached == null ? false : maxReached;
            GPI_PROJECT gpi_project = db.GPI_PROJECT
                    .Include(g => g.GPI_TECHNOLOGY)
                    .Include(g => g.GPI_PROF)
                    .Include(g => g.GPI_PROF1)
                    .Include(g => g.GPI_DOCUMENT)
                    .Include(g => g.GPI_STUDENT)
                    .Where(g => g.PK_Project == id)
                    .Single();

                SetViewBagBoolProperties(gpi_project.PK_Project);

            return View(gpi_project);
        }
        
        [Authorize]
        public ActionResult Download(int id)
        {
            GPI_DOCUMENT doc = db.GPI_DOCUMENT.Find(id);
            byte[] contents = doc.file;
            return File(contents, MIMEHelper.GetMIMEType(doc.name), doc.name);
        }

        [HttpPost]
        [Authorize]
        public ActionResult Upload(int id, HttpPostedFileBase file)
        {
            if (file != null && file.ContentLength > 0)
            {
                var fileName = Path.GetFileName(file.FileName);
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead }))
                {
                    List<GPI_DOCUMENT> list = db.GPI_DOCUMENT.Where(g => g.FK_Project == id && g.name == fileName).ToList();

                    bool newDoc = list.Count == 0;
                    GPI_DOCUMENT doc = newDoc ? new GPI_DOCUMENT() : list[0];

                    var memoryStream = new MemoryStream();
                    file.InputStream.CopyTo(memoryStream);
                    doc.file = memoryStream.ToArray();
                    doc.name = fileName;
                    doc.FK_Project = id;

                    if (newDoc)
                        db.GPI_DOCUMENT.Add(doc);

                    db.SaveChanges();
                    scope.Complete();
                }
            }

            return RedirectToAction("Details", new { id = id });
        }

        //
        // GET: /Project/Create

        [Authorize(Roles = "Prof")]
        public ActionResult Create()
        {
            GPI_PROJECT gpi_project = new GPI_PROJECT();
            gpi_project.title = "Nouveau projet";
            gpi_project.max_students = 1;
            gpi_project.YEAR = DateTime.Now.Year;
            gpi_project.FK_Prof = User.Identity.Name;
            gpi_project.FK_Client = User.Identity.Name;
            db.GPI_PROJECT.Add(gpi_project);
            db.SaveChanges();
            return RedirectToAction("Index");
        } 

        //
        // POST: /Project/Create

        /*[HttpPost]
        [Authorize(Roles = "Prof")]
        public ActionResult Create(GPI_PROJECT gpi_project)
        {
            if (ModelState.IsValid)
            {
                db.GPI_PROJECT.Add(gpi_project);
                db.SaveChanges();
                return RedirectToAction("Index");  
            }

            ViewBag.FK_Prof = new SelectList(db.GPI_PROF, "login", "last_name", gpi_project.FK_Prof);
            ViewBag.FK_Client = new SelectList(db.GPI_PROF, "login", "last_name", gpi_project.FK_Client);
            return View(gpi_project);
        }*/
        
        //
        // GET: /Project/Edit/5

        [Authorize(Roles = "Prof")]
        public ActionResult Edit(int id)
        {

            GPI_PROJECT gpi_project = db.GPI_PROJECT.Find(id);
            ViewBag.FK_Prof = new SelectList(db.GPI_PROF, "login", "last_name", gpi_project.FK_Prof);
            ViewBag.FK_Client = new SelectList(db.GPI_PROF, "login", "last_name", gpi_project.FK_Client);
            GPI_PROJECT project = db.GPI_PROJECT
                .Include(g => g.GPI_TECHNOLOGY)
                .Where(g => g.PK_Project == id)
                .Single();

            PopulateAssignedTechnologyData(project);

            return View(gpi_project);
        }

        private void PopulateAssignedTechnologyData(GPI_PROJECT project)
        {
            var allTechnologies = db.GPI_TECHNOLOGY;

            var projectTechnologies = new HashSet<int>(project.GPI_TECHNOLOGY.Select(t => t.PK_Technology));
            var viewModel = new List<AssignedTechnologyData>();
            foreach (var tech in allTechnologies)
            {
                viewModel.Add(new AssignedTechnologyData
                {
                    TechnologyID = tech.PK_Technology,
                    Title = tech.name,
                    Assigned = projectTechnologies.Contains(tech.PK_Technology)
                });
            }
            ViewBag.Technologies = viewModel;

        }

        //
        // POST: /Project/Edit/5

        [HttpPost, ValidateInput(false)]
        [Authorize(Roles = "Prof")]
        public ActionResult Edit(int id, FormCollection formCollection, string[] selectedTechnologies)
        {
            var projectToUpdate = db.GPI_PROJECT
                .Include(p => p.GPI_TECHNOLOGY)
                .Where(p => p.PK_Project == id)
                .Single();
            if (TryUpdateModel(projectToUpdate, "", null, new string[] { "Technologies" }))
            {
                try
                {
                    UpdateProjectTechnologies(selectedTechnologies, projectToUpdate);

                    db.Entry(projectToUpdate).State = EntityState.Modified;
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                catch (DataException e)
                {
                    //Log the error (add a variable name after DataException)
                    ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
                }

            }
            PopulateAssignedTechnologyData(projectToUpdate);
            ViewBag.FK_Prof = new SelectList(db.GPI_PROF, "login", "last_name", projectToUpdate.FK_Prof);
            ViewBag.FK_Client = new SelectList(db.GPI_PROF, "login", "last_name", projectToUpdate.FK_Client);
            return View(projectToUpdate);
        }

        private void UpdateProjectTechnologies(string[] selectedTechnologies, GPI_PROJECT projectToUpdate)
        {
            if (selectedTechnologies == null)
            {
                projectToUpdate.GPI_TECHNOLOGY = new List<GPI_TECHNOLOGY>();
                return;
            }

            var selectedTechnologiesHS = new HashSet<string>(selectedTechnologies);
            var projectTechnologies = new HashSet<int>(projectToUpdate.GPI_TECHNOLOGY.Select(t => t.PK_Technology));

            foreach (var tech in db.GPI_TECHNOLOGY)
            {
                if (selectedTechnologiesHS.Contains(tech.PK_Technology.ToString()))
                {
                    if (!projectTechnologies.Contains(tech.PK_Technology))
                    {
                        projectToUpdate.GPI_TECHNOLOGY.Add(tech);
                    }
                }
                else 
                {
                    if (projectTechnologies.Contains(tech.PK_Technology))
                    {
                        projectToUpdate.GPI_TECHNOLOGY.Remove(tech);
                    }
                }
            }
        }

        //
        // GET: /Project/Delete/5

        [Authorize(Roles = "Prof")]
        public ActionResult Delete(int id)
        {
            GPI_PROJECT gpi_project = db.GPI_PROJECT.Find(id);
            return View(gpi_project);
        }

        //
        // POST: /Project/Delete/5

        [HttpPost, ActionName("Delete")]
        [Authorize(Roles = "Prof")]
        public ActionResult DeleteConfirmed(int id)
        {            
            GPI_PROJECT gpi_project = db.GPI_PROJECT.Find(id);
            db.GPI_PROJECT.Remove(gpi_project);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        [Authorize(Roles = "Student")]
        public ActionResult Subscribe(int id)
        {
            // TRANSACTION MUST BE SERIALIZABLE HERE
            bool maxReached=false;
            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead }))
            {

                GPI_STUDENT student = db.GPI_STUDENT.Find(User.Identity.Name);
                GPI_PROJECT project = db.GPI_PROJECT
                    .Include(g => g.GPI_STUDENT)
                    .Where(g => g.PK_Project == id)
                    .Single();
                if (project.GPI_STUDENT.Count() < project.max_students)
                {
                    student.FK_Project = id;
                    db.SaveChanges();
                }
                else
                {
                    maxReached = true;
                }
                scope.Complete();
            }
            return RedirectToAction("Details", new { id = id, maxReached = maxReached });
        }

        [Authorize(Roles = "Student")]
        public ActionResult Unsubscribe()
        {
            GPI_STUDENT student = db.GPI_STUDENT.Find(User.Identity.Name);
            int id = student.FK_Project.Value;
            student.FK_Project = null;
            db.SaveChanges();
            return RedirectToAction("Details", new { id = id });
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}